package cn.yangliu.nacos.comm.config.feign;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.Collections;

import cn.yangliu.nacos.comm.JsonResult;
import cn.yangliu.nacos.comm.ex.GlobalException;
import cn.yangliu.nacos.comm.tools.JsonUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.RuntimeJsonMappingException;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import feign.Response;
import feign.Util;
import feign.codec.Decoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The type O auth jackson decoder.
 *
 * @author 问道于盲
 */
public class FeignJacksonDecoder implements Decoder {

	/**
	 * current class's static member
	 * The constant LOGGER.
	 */
	private static final Logger LOGGER = LoggerFactory.getLogger(FeignJacksonDecoder.class);

	/**
	 * current class instance's member.
	 * The Mapper.
	 */
	private final ObjectMapper mapper;

	/**
	 * constructor
	 */
	public FeignJacksonDecoder() {
		this(Collections.emptyList());
	}

	/**
	 * constructor
	 *
	 * @param modules the modules
	 */
	public FeignJacksonDecoder(Iterable<Module> modules) {
		this(JsonUtils.getObjectMapper().registerModules(modules));
	}

	/**
	 * constructor
	 *
	 * @param mapper the mapper
	 */
	public FeignJacksonDecoder(ObjectMapper mapper) {
		LOGGER.info("Using default jackson decoder!");
		this.mapper = mapper;
		mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
		mapper.registerModule(new JavaTimeModule());
	}

	/**
	 * Decode object.
	 *
	 * @param response the response
	 * @param type     the type
	 * @return the object
	 * @throws IOException the io exception
	 * @see Decoder#decode(Response, Type) Decoder#decode(Response, Type)Decoder#decode(Response, Type)feign.codec
	 * .Decoder#decode(feign .Response, java.lang.reflect.Type)
	 */
	@Override
	public Object decode(Response response, Type type) throws IOException {
		if (response.status() == 404) {
			return Util.emptyValueOf(type);
		}
		if (response.body() == null) {
			return null;
		}
		Reader reader = response.body().asReader();
		if (!reader.markSupported()) {
			reader = new BufferedReader(reader, 1);
		}
		try {
			/**
			 * Read the first byte to see if we have any data
			 */
			reader.mark(1);
			if (reader.read() == -1) {
				/**
				 * Eagerly returning null avoids "No content to map due to
				 * end-of-input"
				 */
				return null;
			}
			reader.reset();
			/**
			 * Start Modify
			 */
			JsonResult<?> result = mapper.readValue(reader,
					mapper.constructType(type));
			if (!result.ok()) {
				LOGGER.error("code = {}, message = {}", result.getCode(), result.getMsg());
				throw new GlobalException(result);
			}
			/**
			 * End Modify
			 */
			return result;
		} catch (RuntimeJsonMappingException e) {
			if (e.getCause() instanceof IOException) {
				throw (IOException) e.getCause();
			}
			throw e;
		}
	}

}

